package trabalho3;

import java.util.ArrayList;
import java.util.List;

import org.jtrace.Hit;
import org.jtrace.Jay;
import org.jtrace.Material;
import org.jtrace.NotHit;
import org.jtrace.Section;
import org.jtrace.geometry.GeometricObject;
import org.jtrace.geometry.Sphere;
import org.jtrace.primitives.Vector3D;

public class CSGObject extends GeometricObject{
	Sphere sphere1, sphere2;
	Material material;
	Operation operation;
	

	public CSGObject(Material material, Sphere sphere1, Sphere sphere2, Operation operation) {
		super(material);
		this.material = material;
		this.sphere1 = sphere1;
		this.sphere2 = sphere2;	
		this.operation = operation;
	}


	@Override
	public Hit hit(Jay jay) {
		ArrayList<Section> list = (ArrayList<Section>) this.sections(jay);
		  if(!list.isEmpty()){
		    return list.get(0).getEntryHit();
		  }
		else return new NotHit();
	}


	@Override
	public List<Section> sections(Jay jay) {
		List<Section> sectionList = new ArrayList<Section>();
		
		//inicialização dos Hits (nunca serão utilizados com estes valores!)
		Hit e1 = new Hit(0,new Vector3D(0,0,0));
		Hit e2 = new Hit(0,new Vector3D(0,0,0));
		Hit s1 = new Hit(0,new Vector3D(0,0,0));
		Hit s2 = new Hit(0,new Vector3D(0,0,0));
		
		if (!sphere1.sections(jay).isEmpty()){
			Section sec1 = sphere1.sections(jay).get(0);
			e1 = sec1.getEntryHit();
			s1 = sec1.getExitHit();
		}
		if (!sphere2.sections(jay).isEmpty()){
			Section sec2 = sphere2.sections(jay).get(0);
			e2 = sec2.getEntryHit();
			s2 = sec2.getExitHit();
		}
		
		// 01: caso o raio só passe pelo objeto 1
		if(!sphere1.sections(jay).isEmpty() && sphere2.sections(jay).isEmpty())
		{
			switch(operation){
			case SUBTRACTION_AB:
				sectionList.add(new Section(e1,s1));
				break;
			case UNION:
				sectionList.add(new Section(e1,s1));
				break;
			default:
				break;
			}
		}
		else
		{
			// 02: caso o raio só passe pelo objeto 2
			if(sphere1.sections(jay).isEmpty() && !sphere2.sections(jay).isEmpty())
			{
				switch(operation){
				case SUBTRACTION_BA:
					sectionList.add(new Section(e2,s2));
					break;
				case UNION:
					sectionList.add(new Section(e2,s2));
					break;
				default:
					break;
				}
			}
			else
			{
				// 03: caso o objeto 1 esteja antes do objeto 2 e eles se interceptam
				if(e1.getT()<e2.getT() && e2.getT()<s1.getT() && s1.getT()<s2.getT())
				{
					switch(operation){
						case INTERSECTION:
							sectionList.add(new Section(e2,s1));
							break;
						case SUBTRACTION_AB:
							e2.invertNormal();
							sectionList.add(new Section(e1,e2));
							break;
						case SUBTRACTION_BA:
							s1.invertNormal();
							sectionList.add(new Section(s1,s2));
							break;
						case UNION:
							sectionList.add(new Section(e1,s2));
							break;
						default:
							break;
					}
				}
				else
				{
					// 04: caso o objeto 2 esteja antes do objeto 1 e eles se interceptam
					if(e2.getT()<e1.getT() && e1.getT()<s2.getT() && s2.getT()<s1.getT())
					{
						switch(operation){
							case INTERSECTION:
								sectionList.add(new Section(e1,s2));
								break;
							case SUBTRACTION_AB:
								s2.invertNormal();
								sectionList.add(new Section(s2,s1));
								break;
							case SUBTRACTION_BA:
								e1.invertNormal();
								sectionList.add(new Section(e2,e1));
								break;
							case UNION:
								sectionList.add(new Section(e2,s1));
								break;
							default:
								break;
						}
					}
					else
					{
						// 05: caso o objeto 2 esteja contido dentro do objeto 1
						if(e1.getT()<e2.getT() && s2.getT()<s1.getT())
						{
							switch(operation){
								case INTERSECTION:
									sectionList.add(new Section(e2,s2));
									break;
								case SUBTRACTION_AB:
									e2.invertNormal();
									sectionList.add(new Section(e1,e2));
									s2.invertNormal();
									sectionList.add(new Section(s2,s1));
									break;
								case SUBTRACTION_BA:
									break;
								case UNION:
									sectionList.add(new Section(e1,s1));
									break;
								default:
									break;
							}
						}
						else
						{
							// 06: caso o objeto 1 esteja contido dentro do objeto 2
							if(e2.getT()<e1.getT() && s1.getT()<s2.getT())
							{
								switch(operation){
									case INTERSECTION:
										sectionList.add(new Section(e1,s1));
										break;
									case SUBTRACTION_AB:
										break;
									case SUBTRACTION_BA:
										e1.invertNormal();
										sectionList.add(new Section(e2,e1));
										s1.invertNormal();
										sectionList.add(new Section(s1,s2));
										break;
									case UNION:
										sectionList.add(new Section(e2,s2));
										break;
									default:
										break;
								}
							}
						}
					}
				}
			}
		}
			
		return sectionList;
	}

	
	// Getters and Setters

	public Sphere getSphere1() {
		return sphere1;
	}


	public void setSphere1(Sphere sphere1) {
		this.sphere1 = sphere1;
	}


	public Sphere getSphere2() {
		return sphere2;
	}


	public void setSphere2(Sphere sphere2) {
		this.sphere2 = sphere2;
	}


	public Material getMaterial() {
		return material;
	}


	public void setMaterial(Material material) {
		this.material = material;
	}


	public Operation getOperation() {
		return operation;
	}


	public void setOperation(Operation operation) {
		this.operation = operation;
	}

}
